use actix_web::web::Data;
use chrono::Local;
use sea_orm::{
    prelude::Expr,
    ActiveModelTrait,
    ActiveValue::{NotSet, Set},
    ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, QuerySelect, TransactionTrait,
};

use crate::{
    common::{
        error::ApiError,
        response::ApiResponse,
        types::{ApiListResult, ApiResult},
    },
    config::{
        auth::{AuthInfo, AuthPath},
        state::AppState,
    },
    models::{
        entity::{
            prelude::{Role, RolePerm},
            role, role_perm,
        },
        request::{
            common_request::IdRequest,
            role_req::{RoleBind, RoleCreate, RoleUpdate},
        },
        response::role_res::{RoleDetail, RoleList},
    },
};

pub async fn create(
    state: Data<AppState>,
    req: RoleCreate,
    auth_info: AuthInfo,
) -> ApiResult<bool> {
    let role = Role::find()
        .filter(role::Column::Deleted.eq(false))
        .filter(role::Column::Name.eq(&req.name))
        .one(&state.db)
        .await?;

    if role.is_some() {
        return Err(ApiError::BadRequest("角色名称已存在"));
    }

    let now = Local::now().naive_local();

    let role = role::ActiveModel {
        id: NotSet,
        name: Set(req.name),
        desc: Set(req.desc),
        create_at: Set(now),
        update_at: Set(now),
        create_by: Set(auth_info.user_id),
        update_by: Set(auth_info.user_id),
        deleted: Set(false),
    };

    role.insert(&state.db).await?;

    Ok(ApiResponse::ok(true))
}

pub async fn delete(state: Data<AppState>, req: IdRequest, auth_info: AuthInfo) -> ApiResult<bool> {
    let is_root = auth_info.role_ids.contains(&1);

    if !is_root && !auth_info.role_ids.contains(&req.id) {
        return Err(ApiError::Forbidden("无权限"));
    }

    let role_perm = RolePerm::find()
        .filter(role_perm::Column::Deleted.eq(false))
        .filter(role_perm::Column::RoleId.eq(req.id))
        .all(&state.db)
        .await?;

    if !role_perm.is_empty() {
        return Err(ApiError::Forbidden("请先删除角色权限"));
    }

    let role = Role::find_by_id(req.id)
        .filter(role::Column::Deleted.eq(false))
        .one(&state.db)
        .await?
        .ok_or(ApiError::NotFound("角色不存在"))?;

    let mut role = role.into_active_model();
    role.deleted = Set(true);
    role.update_by = Set(auth_info.user_id);
    role.update_at = Set(Local::now().naive_local());
    role.update(&state.db).await?;

    Ok(ApiResponse::ok(true))
}

pub async fn update(
    state: Data<AppState>,
    req: RoleUpdate,
    auth_info: AuthInfo,
) -> ApiResult<bool> {
    let is_root = auth_info.role_ids.contains(&1);

    if !is_root && !auth_info.role_ids.contains(&req.id) {
        return Err(ApiError::Forbidden("无权限"));
    }

    let role = Role::find_by_id(req.id)
        .filter(role::Column::Deleted.eq(false))
        .one(&state.db)
        .await?
        .ok_or(ApiError::NotFound("角色不存在"))?;

    let mut role = role.into_active_model();
    role.name = Set(req.name);
    role.desc = Set(req.desc);
    role.update_at = Set(Local::now().naive_local());
    role.update_by = Set(auth_info.user_id);

    role.update(&state.db).await?;

    Ok(ApiResponse::ok(true))
}

pub async fn detail(
    state: Data<AppState>,
    req: IdRequest,
    auth_info: AuthInfo,
) -> ApiResult<RoleDetail> {
    let is_root = auth_info.role_ids.contains(&1);

    if !is_root && !auth_info.role_ids.contains(&req.id) {
        return Err(ApiError::Forbidden("无权限"));
    }

    let role = Role::find_by_id(req.id)
        .filter(role::Column::Deleted.eq(false))
        .one(&state.db)
        .await?
        .ok_or(ApiError::NotFound("角色不存在"))?
        .into();

    Ok(ApiResponse::ok(role))
}

pub async fn list(state: Data<AppState>, auth_info: AuthInfo) -> ApiListResult<RoleList> {
    let is_root = auth_info.role_ids.contains(&1);

    let mut select = Role::find()
        .filter(role::Column::Deleted.eq(false))
        .filter(role::Column::Id.eq(1));

    if !is_root {
        select = select.filter(role::Column::Id.is_in(auth_info.role_ids));
    }

    let role = select.all(&state.db).await?;

    let role_list = role.into_iter().map(RoleList::from).collect();

    Ok(ApiResponse::ok(role_list))
}

pub async fn bind(state: Data<AppState>, req: RoleBind, auth_info: AuthInfo) -> ApiResult<bool> {
    let is_root = auth_info.role_ids.contains(&1);

    if !is_root {
        if !auth_info.role_ids.contains(&req.id) {
            return Err(ApiError::Forbidden("无权限"));
        }

        let perm_ids = RolePerm::find()
            .select_only()
            .column(role_perm::Column::PermId)
            .filter(role_perm::Column::RoleId.is_in(auth_info.role_ids))
            .into_tuple()
            .all(&state.db)
            .await?;

        if req.perm_ids.iter().any(|id| !perm_ids.contains(id)) {
            return Err(ApiError::Forbidden("无权限"));
        }
    }

    let mut role_perm = vec![];

    let now = Local::now().naive_local();

    for pid in req.perm_ids {
        role_perm.push(role_perm::ActiveModel {
            id: NotSet,
            role_id: Set(req.id),
            perm_id: Set(pid),
            create_at: Set(now),
            update_at: Set(now),
            create_by: Set(auth_info.user_id),
            update_by: Set(auth_info.user_id),
            deleted: Set(false),
        });
    }

    let txn = state.db.begin().await?;

    RolePerm::update_many()
        .col_expr(role_perm::Column::Deleted, Expr::value(true))
        .col_expr(role_perm::Column::UpdateBy, Expr::value(auth_info.user_id))
        .col_expr(role_perm::Column::UpdateAt, Expr::value(now))
        .filter(role_perm::Column::RoleId.eq(req.id))
        .exec(&txn)
        .await?;

    RolePerm::insert_many(role_perm).exec(&txn).await?;

    txn.commit().await?;

    AuthPath::init(&state).await?;

    Ok(ApiResponse::ok(true))
}
